/**************************************************************************************************
	$Header: /pub/cvsroot/mydns/src/util/import-tinydns.c,v 1.4 2002/09/12 04:50:42 bboy Exp $
	import-tinydns.c: Import DNS data from a tinydns-data format data file.

	Copyright (C) 2002  Don Moore <bboy@bboy.net>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at Your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**************************************************************************************************/

#include "util.h"

#ifdef TINYDNS_IMPORT

#define MAX_FIELDS	20											/* Max fields per line */
#define TINYDNS_DEF_REFRESH	16384
#define TINYDNS_DEF_RETRY		2048
#define TINYDNS_DEF_EXPIRE		1048576
#define TINYDNS_DEF_MINIMUM	2560
#define TINYDNS_DEF_TTL			3600


static FILE *tinydns;											/* Input file pointer */
static char *field[MAX_FIELDS];								/* Fields parsed from line */
static unsigned int lineno;									/* Current line number */
static char *filename;											/* Input file name */
static char def_serial[80];									/* Default serial */


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	OUTPUT_SOA
	Creates the specified SOA record.  Assigns the SOA id to the MySQL-variable `soa_id'.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
output_soa(
	char *origin, char *ns, char *mbox, char *serial, char *refresh, char *retry,
	char *expire, char *minimum, char *ttl)
{
	char xns[BUFSIZ], xmbox[BUFSIZ];

	if (!origin)
		return;

	if (ns)
		strncpy(xns, ns, sizeof(xns)-1);
	else
		snprintf(xns, sizeof(xns), "ns.%s", origin);

	if (mbox)
		strncpy(xmbox, mbox, sizeof(xmbox)-1);
	else
		snprintf(xmbox, sizeof(xmbox), "hostmaster.%s", origin);

	printf(
		"INSERT IGNORE INTO %s"
		" (origin,ns,mbox,serial,refresh,retry,expire,minimum,ttl)"
		" VALUES ('%s.','%s.','%s.',%u,%u,%u,%u,%u,%u);\n", mydns_soa_table_name,
		origin, xns, xmbox,
		serial ? (u_int32_t)atol(serial) : (u_int32_t)atol(def_serial),
		refresh ? (u_int32_t)atol(refresh) : TINYDNS_DEF_REFRESH,
		retry ? (u_int32_t)atol(retry) : TINYDNS_DEF_RETRY,
		expire ? (u_int32_t)atol(expire) : TINYDNS_DEF_EXPIRE,
		minimum ? (u_int32_t)atol(minimum) : TINYDNS_DEF_MINIMUM,
		ttl ? (u_int32_t)atol(ttl) : TINYDNS_DEF_TTL);
	printf("SELECT @soa_id:=id FROM %s WHERE origin='%s';\n", mydns_soa_table_name, origin);
}
/*--- output_soa() ------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	OUTPUT_RR
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
output_rr(char *name, char *type, char *data, char *aux, char *ttl)
{
	u_int16_t nttl = ttl ? atoi(ttl) : TINYDNS_DEF_TTL;

	if (!name || !type || !data)
		return;

	printf(
		"REPLACE INTO %s"
		" (zone,name,type,data,aux,ttl)"
		" VALUES (@soa_id,'%s','%s','%s',%u,%u);\n", mydns_rr_table_name,
		name, type, data,
		aux ? (u_int32_t)atol(aux) : 0,
		nttl < 60 ? TINYDNS_DEF_TTL : nttl);
}
/*--- output_rr() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_DOT

	.fqdn:ip:x:ttl:timestamp:lo

	Name server for our domain fqdn. tinydns-data creates

	*	an NS record showing x.ns.fqdn as a name server for fqdn;
	*	an A record showing ip as the IP address of x.ns.fqdn; and
	*	an SOA record for fqdn listing x.ns.fqdn as the primary name server
		and hostmaster@fqdn as the contact address.

	You may have several name servers for one domain, with a different x for each server.
	tinydns will return only one SOA record per domain.

	If x contains a dot then tinydns-data will use x as the server name rather than x.ns.fqdn.
	This feature is provided only for compatibility reasons; names not ending with fqdn will
	force clients to contact parent servers much more often than they otherwise would, and will
	reduce the overall reliability of DNS. You should omit ip if x has IP addresses assigned
	elsewhere in data; in this case, tinydns-data will omit the A record. 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_dot(void)
{
	char	*fqdn			= field[0],
			*ip			= field[1],
			*x				= field[2],
			*ttl			= field[3],
			*timestamp	= field[4],
			*lo			= field[5];
	char	xns[BUFSIZ];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));

	if (x && *x)
	{
		if (strchr(x, '.'))
			strncpy(xns, x, sizeof(xns)-1);
		else
			snprintf(xns, sizeof(xns), "%s.ns.%s", x, fqdn);
	}

	/* Create SOA */
	output_soa(fqdn, (x && *x) ? xns : NULL, NULL, NULL, NULL, NULL, NULL, NULL, ttl);

	/* Create A record showing "ip" as the IP address of "xns" */
	if (x && *x && ip && *ip)
		output_rr(xns, "A", ip, 0, ttl);
}
/*--- tinydns_dot() -----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_AMP

	&fqdn:ip:x:ttl:timestamp:lo

	Name server for domain fqdn. tinydns-data creates

	*	an NS record showing x.ns.fqdn as a name server for fqdn and
	*	an A record showing ip as the IP address of x.ns.fqdn. 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_amp(void)
{
	char	*fqdn			= field[0],
			*ip			= field[1],
			*x				= field[2],
			*ttl			= field[3],
			*timestamp	= field[4],
			*lo			= field[5];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));
}
/*--- tinydns_amp() -----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_EQUAL

	=fqdn:ip:ttl:timestamp:lo

	Host fqdn with IP address ip. tinydns-data creates

	*	an A record showing ip as the IP address of fqdn and
	*	a PTR record showing fqdn as the name of d.c.b.a.in-addr.arpa if ip is a.b.c.d.

	Remember to specify name servers for some suffix of fqdn; otherwise tinydns will not
	respond to queries about fqdn. The same comment applies to other records described below.
	Similarly, remember to specify name servers for some suffix of d.c.b.a.in-addr.arpa,
	if that domain has been delegated to you. 
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_equal(void)
{
	char	*fqdn			= field[0],
			*ip			= field[1],
			*ttl			= field[2],
			*timestamp	= field[3],
			*lo			= field[4];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));
}
/*--- tinydns_equal() ---------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_PLUS

	+fqdn:ip:ttl:timestamp:lo

	Alias fqdn with IP address ip. This is just like =fqdn:ip:ttl except that tinydns-data
	does not create the PTR record.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_plus(void)
{
	char	*fqdn			= field[0],
			*ip			= field[1],
			*ttl			= field[2],
			*timestamp	= field[3],
			*lo			= field[4];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));
}
/*--- tinydns_plus() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_AT

	@fqdn:ip:x:dist:ttl:timestamp:lo

	Mail exchanger for fqdn. tinydns-data creates

	*	an MX record showing x.mx.fqdn as a mail exchanger for fqdn at distance dist and
	*	an A record showing ip as the IP address of x.mx.fqdn.

	You may omit dist; the default distance is 0.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_at(void)
{
	char	*fqdn			= field[0],
			*ip			= field[1],
			*x				= field[2],
			*dist			= field[3],
			*ttl			= field[4],
			*timestamp	= field[5],
			*lo			= field[6];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));
}
/*--- tinydns_at() ------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_APOS

	'fqdn:s:ttl:timestamp:lo

	TXT record for fqdn. tinydns-data creates a TXT record for fqdn containing the
	string s. You may use octal \nnn codes to include arbitrary bytes inside s;
	for example, \072 is a colon.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_apos(void)
{
	char	*fqdn			= field[0],
			*s				= field[1],
			*ttl			= field[2],
			*timestamp	= field[3],
			*lo			= field[4];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));
}
/*--- tinydns_apos() ----------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_CARET

	^fqdn:p:ttl:timestamp:lo

	PTR record for fqdn. tinydns-data creates a PTR record for fqdn pointing to the domain name p.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_caret(void)
{
	char	*fqdn			= field[0],
			*p				= field[1],
			*ttl			= field[2],
			*timestamp	= field[3],
			*lo			= field[4];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));
}
/*--- tinydns_caret() ---------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_C

	Cfqdn:p:ttl:timestamp:lo

	CNAME record for fqdn. tinydns-data creates a CNAME record for fqdn
	pointing to the domain name p.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_C(void)
{
	char	*fqdn			= field[0],
			*p				= field[1],
			*ttl			= field[2],
			*timestamp	= field[3],
			*lo			= field[4];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));
}
/*--- tinydns_C() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_Z

	Zfqdn:ns:mbox:ser:ref:ret:exp:min:ttl:timestamp:lo

	SOA record for fqdn showing ns as the primary name server,
	mbox (with the first . converted to @) as the contact address,
	ser as the serial number, ref as the refresh time, ret as the retry time,
	exp as the expire time, and min as the minimum time.
	ser, ref, ret, exp, and min may be omitted; they default to, respectively,
	the modification time of the data file, 16384 seconds, 2048 seconds,
	1048576 seconds, and 2560 seconds
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_Z(void)
{
	char	*fqdn			= field[0],
			*ns			= field[1],
			*mbox			= field[2],
			*serial		= field[3],
			*refresh		= field[4],
			*retry		= field[5],
			*expire		= field[6],
			*minimum		= field[7],
			*ttl			= field[8],
			*timestamp	= field[9],
			*lo			= field[10];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));

	output_soa(fqdn, ns, mbox, serial, refresh, retry, expire, minimum, ttl);
}
/*--- tinydns_Z() -------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	TINYDNS_COLON

	:fqdn:n:rdata:ttl:timestamp:lo

	Generic record for fqdn. tinydns-data creates a record of type n for fqdn showing rdata.
	n must be an integer between 1 and 65535. The proper format of rdata depends on n.
	You may use octal \nnn codes to include arbitrary bytes inside rdata
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static void
tinydns_colon(void)
{
	char	*fqdn			= field[0],
			*n				= field[1],
			*rdata		= field[2],
			*ttl			= field[3],
			*timestamp	= field[4],
			*lo			= field[5];

	if (!fqdn)
		return (void)Warnx("%s:%u: %s", filename, lineno, _("fqdn field empty"));
}
/*--- tinydns_colon() ---------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	IMPORT_TINYDNS
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
import_tinydns(char *datafile, char *import_zone)
{
	struct stat st;
	char buf[4096], *b;
	int n;

	lineno = 0;
	filename = datafile;
	if (stat(filename, &st))
		return (void)Warn("%s", filename);

	/* Set default serial (for all zones) */
	strftime(def_serial, sizeof(def_serial)-1, "%Y%m%d", localtime(&st.st_mtime));

	if (!(tinydns = fopen(filename, "r")))
		return (void)Warn("%s", filename);
	while (fgets(buf, sizeof(buf), tinydns))
	{
		lineno++;
		strtrim(buf);
		if (strlen(buf) < 2)
			continue;
		printf("/* Line %u: \"%s\" */\n", lineno, buf);
		for (n = 0; n < MAX_FIELDS; n++)						/* Reset fields */
			field[n] = (char *)NULL;
		for (n = 0, b = buf + 1; n < MAX_FIELDS; n++)	/* Load fields */
			if (!(field[n] = strsep(&b, ":")))
				break;
		switch (buf[0])
		{
			case '.':
				tinydns_dot();
				break;

			case '&':
				tinydns_amp();
				break;

			case '=':
				tinydns_equal();
				break;

			case '+':
				tinydns_plus();
				break;

			case '@':
				tinydns_at();
				break;

			case '#':		/* Comment line */
				break;

			case '-':		/* Ignored (by tinydns-data) */
				break;

			case '\'':
				tinydns_apos();
				break;

			case '^':
				tinydns_caret();
				break;

			case 'C':
				tinydns_C();
				break;

			case 'Z':
				tinydns_Z();
				break;

			case ':':
				tinydns_colon();
				break;

			default:
				Warnx("%s:%u: %s (0x%X)", filename, lineno, _("unknown line type"), buf[0]);
				break;
		}
		printf("\n");
	}
	fclose(tinydns);
}
/*--- import_tinydns() --------------------------------------------------------------------------*/

#endif /* TINYDNS */

/* vi:set ts=3: */
/* NEED_PO */
